package com.tanhua.dubbo.server.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;

import com.alibaba.dubbo.config.annotation.Service;
import com.mongodb.client.result.DeleteResult;

import com.tanhua.dubbo.server.enums.IdType;
import com.tanhua.dubbo.server.pojo.FollowUser;
import com.tanhua.dubbo.server.pojo.Video;
import com.tanhua.dubbo.server.service.IdService;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created with IntelliJ IDEA.
 *
 * @ClassName: VideoApiImpl
 * @Author:王晨晗
 * @Description: 小视频功能接口的实现类
 * @Date: 2022/01/03/11:33
 */
@Service
@Slf4j
public class VideoApiImpl implements VideoApi {
    //reids中推荐小视频的键值前缀
    private static final String VIDEO_FOLLOW_USER_KEY_PREFIX = "VIDEO_FOLLOW_USER_";

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private IdService idService;

    /**
     * @param video 视频对象
     * @return 保存是否成功
     * @description 保存视频
     */
    @Override
    public Boolean saveVideo(Video video) {
        try {
            //设置视频的自增长id
            video.setVid(idService.createId(IdType.VIDEO));
            //保存视频信息
            mongoTemplate.save(video);
        } catch (Exception e) {
            log.error("视频发布失败, video :" + video, e);
            return false;
        }
        return true;
    }

    /**
     * @param userId   当前用户id
     * @param page     页数
     * @param pageSize 条数
     * @return 小视频信息集合
     * @description 查询视频列表
     */
    @Override
    public List<Video> queryVideoList(Long userId, Integer page, Integer pageSize) {
        //从redis中获取推荐的小视频的数据
        String redisKey = "QUANZI_VIDEO_RECOMMEND_" + userId;
        String redisData = redisTemplate.opsForValue().get(redisKey);
        //创建存放视频id的long集合
        List<Long> vids = new ArrayList<>();
        int recommendCount = 0;     //推荐小视频的总数
        //判断redis中是否有值
        if (StrUtil.isNotEmpty(redisData)) {
            //获取redis中的推荐视频的vid
            List<String> vidList = StrUtil.split(redisData, ",");
            //计算分页
            int[] startEnd = PageUtil.transToStartEnd(page - 1, pageSize); //此数组存放的是索引信息[开始, 结束]
            int startIndex = startEnd[0];  //开始
            int endIndex = Math.min(startEnd[1], vidList.size());   //结束
            //获取到每一页的推荐视频id;
            for (int i = startIndex; i < endIndex; i++) {
                vids.add(Convert.toLong(vidList.get(i)));
            }
            //保存推荐小视频的总数
            recommendCount = vidList.size();
        }

        //判断vids集合是否有数据 , 没有则表示推荐视频已读取完,或没有推荐视频;此时进行数据库查询视频
        if (CollUtil.isEmpty(vids)) {
            List<Long> ids = new ArrayList<>();
            //对于已经推荐的视频进行排除;
            if (StrUtil.isNotEmpty(redisData)) {
                //获取已经推荐的小视频的id
                ids = StrUtil.split(redisData, ",").stream().map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
            }

            int pageTotal = PageUtil.totalPage(recommendCount, pageSize);      //推荐视频共几页
            //分页条件
            PageRequest pageRequest = PageRequest.of(page - pageTotal - 1, pageSize, Sort.by(Sort.Order.desc("created")));
            //查询条件
            Query query = new Query(Criteria.where("vid").nin(ids)).with(pageRequest);
            //获取集合信息;
            List<Video> videoList = mongoTemplate.find(query, Video.class);
            //返回视频列表
            return videoList;
        }

        //查询推荐的小视频
        Query query = Query.query(Criteria.where("vid").in(vids)).with(Sort.by(Sort.Order.desc("created")));
        List<Video> videoList = mongoTemplate.find(query, Video.class);
        //返回推荐的小视频
        return videoList;
    }

    /**
     * @param userId       当前用户id
     * @param followUserId 被关注的id;
     * @return
     * @description 关注用户
     */
    @Override
    public Boolean followUser(Long userId, Long followUserId) {
        //判断用户是否关注
        if (queryIsFollow(userId, followUserId)) {
            return false;   //关注了,返回false(关注失败)
        }

        try {
            //保存关注信息
            FollowUser followUser = new FollowUser();
            //封装关注数据
            followUser.setId(ObjectId.get());       //关注表id
            followUser.setUserId(userId);       //用户id
            followUser.setFollowUserId(followUserId);   //关注的用户id
            followUser.setCreated(System.currentTimeMillis());  //关注时间
            //保存到关注表
            mongoTemplate.save(followUser);

            //保存数据到redis中
            String redisKey = VIDEO_FOLLOW_USER_KEY_PREFIX + userId;
            String hashKey = String.valueOf(followUserId);
            redisTemplate.opsForHash().put(redisKey, hashKey, "1");
            //关注成功
            return true;
        } catch (Exception e) {
            log.error("关注失败", e);
        }
        //关注失败
        return false;
    }

    /**
     * @param userId       当前用户id
     * @param followUserId 被关注者id;
     * @return
     * @description 取消关注
     */
    @Override
    public Boolean disFollowUser(Long userId, Long followUserId) {
        //判断用户是否关注
        if (!queryIsFollow(userId, followUserId)) {
            return false;  //未关注,则取消关注失败,返回false
        }
        //删除关注表中对应的关注信息;
        //构建查询条件
        Query query = Query.query(Criteria.where("userId").is(userId).and("followUserId").is(followUserId));
        //执行删除
        DeleteResult result = mongoTemplate.remove(query, FollowUser.class);
        //判断是否删除成功
        if (result.getDeletedCount() > 0) {
            //获取redisKey
            String redisKey = VIDEO_FOLLOW_USER_KEY_PREFIX + userId;
            String hashKey = String.valueOf(followUserId);
            //删除redis中对应用户关注信息;
            redisTemplate.opsForHash().delete(redisKey, hashKey);
            //返回取消关注成功;
            return true;
        }
        //删除失败,返回false;
        return false;
    }

    /**
     * @param userId       当前用户id
     * @param followUserId 被关注的用户id
     * @return
     * @description 查询用户是否关注
     */
    @Override
    public Boolean queryIsFollow(Long userId, Long followUserId) {
        //redis中key
        String redisKey = VIDEO_FOLLOW_USER_KEY_PREFIX + userId;
        String hashKey = String.valueOf(followUserId);
        //获取redis中的值
        Object value = redisTemplate.opsForHash().get(redisKey, hashKey);
        //判断值是否为空
        if (ObjectUtil.isNotEmpty(value)) {
            //不为空,判断值是否是1 , 值为1表示关注;
            return redisTemplate.opsForHash().get(redisKey, hashKey).equals("1");
        }

        //redis值为空, 查询数据库,查看用户是否关注
        Query query = Query.query(Criteria.where("userId").is(userId).and("followUserId").is(followUserId));
        long count = mongoTemplate.count(query, FollowUser.class);
        //判断查询到条数
        if (count == 0) {
            return false;       //为0 ,表示未关注,返回false;
        }
        //将用户关注信息,存入redis中
        redisTemplate.opsForHash().put(redisKey, hashKey, "1");
        //返回关注结果
        return true;
    }

    /**
     * @param videoId
     * @return
     * @description 根据视频id 查询视频信息
     */
    @Override
    public Video queryVideoById(String videoId) {
        Query query = Query.query(Criteria.where("id").is(videoId));
        return mongoTemplate.findOne(query, Video.class);
    }

    //根据id查询指定用户的视频
    @Override
    public List<Video> findByUserId(Long uid, Integer page, Integer pagesize, String sortProp, String sortOrder) {
        PageRequest pageRequest;
        //判断顺序还是倒序排列
        if ("ascending".equals(sortOrder)) {   //如果相同就顺序排列
            pageRequest = PageRequest.of(page - 1, pagesize, Sort.by(Sort.Order.asc(sortProp)));//顺序排序
        } else {
            pageRequest = PageRequest.of(page - 1, pagesize, Sort.by(Sort.Order.desc(sortProp)));//倒序排序

        }


        Query query = new Query(Criteria.where("userId").in(uid)).with(pageRequest);

        //获取集合信息;
        List<Video> videoList = mongoTemplate.find(query, Video.class);
        //返回视频列表
        return videoList;
    }


}
